﻿using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace WH.Common.Cache
{
    public class MemoryCaching : ICaching
    {
        private readonly IMemoryCache _cache;

        private const int default_cache_duration_value = 7200;

        //还是通过构造函数的方法，获取
        public MemoryCaching(IMemoryCache cache)
        {
            _cache = cache;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="cacheDurationInSeconds"></param>
        public void Add<V>(string key, V value, int cacheDurationInSeconds = default_cache_duration_value)
        {
            _cache.Set(key, value, TimeSpan.FromSeconds(cacheDurationInSeconds));
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="cacheDurationInTimeSpan"></param>
        public void Add<V>(string key, V value, TimeSpan cacheDurationInTimeSpan)
        {
            _cache.Set(key, value, cacheDurationInTimeSpan);
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Add<V>(string key, V value)
        {
            Add(key, value, 7200);
        }


        /// <summary>
        /// 是否存在
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool ContainsKey<V>(string key)
        {
            var keys = GetAllKey(key);

            return keys.Any();
        }

        /// <summary>
        /// 获取
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public V Get<V>(string key)
        {
            return _cache.Get<V>(key);
        }

        /// <summary>
        /// 获取全部key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IEnumerable<string> GetAllKey(string key = "")
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var entries = _cache.GetType().GetField("_entries", flags)?.GetValue(_cache);
            var keys = new List<string>();
            if (entries is not IDictionary cacheItems)
            {
                return keys;
            }

            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                var cacheKey = cacheItem.Key.ToString();

                if (cacheKey is null)
                {
                    continue;
                }

                if (!string.IsNullOrEmpty(key) && cacheKey.Contains(key) != true)
                {
                    continue;
                }

                keys.Add(cacheKey);
            }
            return keys;
        }

        /// <summary>
        /// 获取或者创建
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="cacheKey"></param>
        /// <param name="create"></param>
        /// <param name="cacheDurationInSeconds"></param>
        /// <returns></returns>
        public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = default_cache_duration_value)
        {
            if (ContainsKey<V>(cacheKey))
            {
                return Get<V>(cacheKey);
            }
            else
            {
                var res = create();
                Add<V>(cacheKey, res, cacheDurationInSeconds == int.MaxValue ? default_cache_duration_value : cacheDurationInSeconds);
                return res;
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
        {
            _cache.Remove(key);
        }

        /// <summary>
        /// 如果包含则删除
        /// </summary>
        /// <param name="typeName"></param>
        public void ContainsRemove(string typeName)
        {
            var keyList = GetAllKey(typeName);

            foreach (var key in keyList)
            {
                Remove(key);
            }
        }

        public string Info()
        {
            return "MemoryCache";
        }

        /// <summary>
        /// 获取所有
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <returns></returns>
        public IEnumerable<string> GetAllKey<V>()
        {
            return GetAllKey();
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="V"></typeparam>
        /// <param name="key"></param>
        public void Remove<V>(string key)
        {
            Remove(key);
        }

        public Task<bool> HMSetAsync<V>(string key, IDictionary<string, V> value)
        {
            var b = _cache.TryGetValue(key, out IDictionary<string, V> ovalue);

            if (b)
            {
                foreach (var kv in value)
                {
                    if (ovalue.ContainsKey(kv.Key))
                    {
                        ovalue[kv.Key] = kv.Value;
                    }
                    else
                    {
                        ovalue.Add(kv.Key, kv.Value);
                    }

                }
            }
            else
            {
                Add(key, value, default_cache_duration_value);
            }

            return Task.FromResult(true);
        }

        public Task<Dictionary<string, V>> HGetAllAsync<V>(string key)
        {
            var res = _cache.Get<Dictionary<string, V>>(key);
            return Task.FromResult(res);
        }

        public Task<V?> HGetAsync<V>(string key, string field)
        {
            var dic = _cache.Get<Dictionary<string, V>>(key);

            if (dic is not null)
            {
                dic.TryGetValue(field, out V? result);
                return Task.FromResult(result);
            }
            else
            {
                return Task.FromResult<V?>(default);
            }
        }
    }
}

